#include <regex>

#include <opencv2/core/utils/logger.hpp>

#include "Renderer.h"

#include "BackgroundPicture.h"
#include "Caption.h"
#include "InstrumentBackground.h"
#include "InstrumentLabels.h"
#include "NotesManager.h"
#include "ParticleManager.h"
#include "RecordPicture.h"

#include "MIDIReader.h"

#include "Drawer.h"

void render(
	std::string config_path, 
	std::string midi_path, 
	std::string output_path, 
	std::string bg_picture_path, 
	std::string record_path, 
	std::string audio_path,
	std::string lyric_path,
	AudioFormat audio_format
) {
	json _check_colors(json& j, std::vector<std::string>& names, std::string config_path);
	Double2 _json_to_double2(json & j);
	Location _json_to_int2(json & j);
	Color _json_to_uchar3(json & j);
	json j = read_json(config_path);
	double frame_rate = j["video"]["frame_rate"];
	size_t begin_secs = j["video"]["begin_secs"];
	size_t end_secs = j["video"]["end_secs"];
	double velocity_blur_sigma = j["midi"]["blur_sigma"];
	NestedMap colors = color_to_map(j);
	int frame_size_x = j["video"]["frame_size"][0];
	int frame_size_y = j["video"]["frame_size"][1];
	cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_ERROR);
	auto [note_streams, velocity_streams] = read_midi(midi_path, frame_rate, begin_secs, end_secs, velocity_blur_sigma, j["note_size"]);
	std::vector<std::string> names;
	Drawer::initGL(frame_size_x, frame_size_y);
	FT_Library _font_library;
	if (FT_Init_FreeType(&_font_library)) {
		throw "加载FreeType库失败。";
	}
	std::vector<MLObj*> obj_list;
	for (size_t i = 1; i < velocity_streams.size(); i++) {
		names.push_back(velocity_streams[i].name);
	}
	j = _check_colors(j, names, config_path);
	if (bg_picture_path != "") {
		BackgroundPicture* bgPicture = new BackgroundPicture(
			frame_size_x,
			frame_size_y,
			frame_rate,
			bg_picture_path,
			j["background"]["alpha"]
		);
		obj_list.push_back(bgPicture);
	}
	if (j["light"]["enable"]) {
		InstrumentBackground* background = new InstrumentBackground(
			velocity_streams,
			frame_size_x, 
			frame_size_y,
			frame_rate,
			colors,
			j["light"]["alpha"],
			j["light"]["light_extension"],
			j["light"]["min_light_extension_angle"],
			j["light"]["brightness"],
			record_path != "" ? (int)j["record"]["body_radius"] : 0
		);
		obj_list.push_back(background);
	}
	if (j["notes"]["enable"]) {
		NotesManager* notes = new NotesManager(
			frame_size_x, 
			frame_size_y,
			frame_rate,
			note_streams,
			colors,
			j["notes"]["alpha"],
			j["notes"]["play_speed"],
			record_path != "" ? (int)j["record"]["body_radius"] : 0,
			j["notes"]["border_brightness"],
			j["notes"]["center_brightness"],
			j["notes"]["border_radius"],
			j["notes"]["middle_color_loc"]
		);
		obj_list.push_back(notes);
	}
	if (j["particles"]["enable"]) {
		ParticleManager* particles = new ParticleManager(
			frame_size_x, 
			frame_size_y,
			frame_rate,
			velocity_streams,
			colors,
			j["particles"]["brightness"],
			j["particles"]["alpha"],
			_json_to_double2(j["particles"]["lifespan_range"]),
			_json_to_int2(j["particles"]["radius_range"]),
			_json_to_double2(j["particles"]["v_range"]),
			_json_to_double2(j["particles"]["a_delta_range"]),
			_json_to_double2(j["particles"]["init_angle_range"]),
			_json_to_double2(j["particles"]["a_angle_range"]),
			j["particles"]["blur_sigma"],
			j["particles"]["sparseness"],
			record_path != "" ? (int)j["record"]["body_radius"] : 0,
			j["particles"]["trailing_decay"]
		);
		obj_list.push_back(particles);
	}
	if (j["record"]["body_radius"] > 0 && record_path != "") {
		RecordPicture* record = new RecordPicture(
			frame_size_x,
			frame_size_y,
			frame_rate,
			_json_to_uchar3(j["record"]["body_color"]),
			j["record"]["body_radius"],
			record_path,
			j["record"]["picture_radius"],
			_json_to_uchar3(j["record"]["center_color"]),
			j["record"]["center_radius"],
			j["record"]["rotate_speed"],
			j["record"]["brightness"]
		);
		obj_list.push_back(record);
	}
	if (j["labels"]["enable"]) {
		InstrumentLabels* labels = new InstrumentLabels(
			frame_size_x, 
			frame_size_y,
			frame_rate,
			velocity_streams,
			j["labels"]["font_size"],
			j["labels"]["font_path"][0],
			j["labels"]["font_path"][1],
			j["labels"]["line_spacing"],
			_json_to_uchar3(j["labels"]["color"]),
			j["labels"]["alpha"],
			_font_library
		);
		obj_list.push_back(labels);
	}
	if (lyric_path != "") {
		Caption* caption = new Caption(
			frame_size_x,
			frame_size_y,
			frame_rate,
			begin_secs,
			end_secs,
			velocity_streams[0].length,
			j["caption"]["location"][0],
			j["caption"]["location"][1],
			lyric_path,
			j["caption"]["font_size"],
			j["caption"]["font_path"][0],
			j["caption"]["font_path"][1],
			_json_to_uchar3(j["caption"]["color"]),
			j["caption"]["alpha"],
			j["caption"]["anchor_type"],
			j["caption"]["anchor_loc"][0],
			j["caption"]["anchor_loc"][1],
			j["caption"]["horizontal"],
			j["caption"]["line_spacing"],
			j["caption"]["alignment_type"],
			_font_library
		);
		obj_list.push_back(caption);
	}
	AudioProcessor audio_processor;
	if (audio_path != "") {
		audio_processor.generate(
			audio_path,
			audio_format,
			j["video"]["begin_secs"],
			j["audio"]["sample_rate"],
			j["audio"]["aac_bit_rate"],
			j["audio"]["flac_bit_width"],
			j["audio"]["flac_compression_level"]
		);
	}
	MLRender render(
		output_path,
		frame_rate,
		frame_size_x, 
		frame_size_y,
		velocity_streams[0].length,
		j["video"]["quality"],
		audio_processor
	);
	render.render(obj_list);
	std::cout << "已完成视频写入。" << std::endl;
	FT_Done_FreeType(_font_library);
}

json _check_colors(json& j, std::vector<std::string>& names, std::string config_path) {
	bool color_vacancy = false;
	for (std::string name : names) {
		if (!j["colors"].contains(name)) {
			printf("[ERROR]乐器%s的颜色空缺，请在配置中补全。\n", name.c_str());
			color_vacancy = true;
		}
	}
	if (color_vacancy) {
		throw "缺少一种或多种乐器的颜色。";
	}
	return j;
}

Double2 _json_to_double2(json& j) {
	std::vector<double> data = j;
	Double2 result = {
		.x = data[0],
		.y = data[1]
	};
	return result;
}

Location _json_to_int2(json& j) {
	std::vector<int> data = j;
	Location result = {
		.x = data[0],
		.y = data[1]
	};
	return result;
}

Color _json_to_uchar3(json& j) {
	std::vector<Byte> data = j;
	Color result = {
		.x = data[0],
		.y = data[1],
		.z = data[2]
	};
	return result;
}
